package cn.biplam.back.sql.type;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;

import cn.biplam.common.wrapper.ErrorMessage;

/**
 * Decimal数据元(高精度浮点)
 * <li> 数据长度(0-38)
 * <li> 数据精度(-6-32)
 * @author 	WangSong
 * @create 	Mar 13, 2009 11:39:27 AM
 * @version 1.0
 */
public class Decimal implements Type,Serializable{

	private static final long serialVersionUID = -1184068705984351555L;
	private static final String sTypeName="DECIMAL";
	/** Decimal数据元最大长度 */
	private static final int DECIMAL_MAX_PRECISION=38;
	/** Decimal数据元最大小数位数 */
	private static final int DECIMAL_MAX_SCALE=127;
	/** Decimal数据元最小小数位数 */
	private static final int DECIMAL_MIN_SCALE=-84;

	private boolean bIsNull=true;
	/** 默认长度 */
	private int iPrecision=38;
	/** 默认精度 */
	private int iScale=32;
	private String sValue=null;

	/**
	 * 默认构造函数，默认长度为38、精度为32
	 */
	public Decimal(){}

	/**
	 * 创建Decimal数据元
	 * @param precision 长度<=38
	 * @throws ErrorMessage
	 */
	public Decimal(int precision) throws ErrorMessage{
		this.setLength(precision);
	}
	
	/**
	 * 构造方法，设置数据长度和精度
	 * @param persion 长度<=38
	 * @param scale 精度
	 * @throws ErrorMessage
	 */
	public Decimal(int persion,int scale) throws ErrorMessage{
		this.setLength(persion);
		this.setScale(scale);

	}

	/**
	 *  构造方法，设置数据长度、精度和值
	  * @param persion 长度<=127
	 * @param scale 精度
	 * @param value 默认值
	 * @throws ErrorMessage
	 */
	public Decimal(int persion,int scale,String value) throws ErrorMessage{
		this.setLength(persion);
		this.setScale(scale);
		this.setValue(value);
	}

	/**
	 * 返回数据类型完整描述
	 * @return
	 */
	public String getType() {
		StringBuilder sp=new StringBuilder();
		sp.append(sTypeName);
		sp.append("(");
		sp.append(this.iPrecision);
		if(this.iScale!=0){
			sp.append(",");
			sp.append(this.iScale);
		}
		sp.append(")");
		return sp.toString();
	}

	/**
	 * 返回类型名称
	 * @return
	 */
	@SuppressWarnings("static-access")
	public String getTypeName() {
		return this.sTypeName;
	}

	/**
	 * 在值不为空时返回值
	 * @return
	 * @throws ErrorMessage
	 */
	public String getValue() throws ErrorMessage {
		if(this.isNull()){
			ErrorMessage eMsg=new ErrorMessage("Number type is empty!");
			throw eMsg;
		}
		return this.sValue;
	}

	/**
	 * 返回是否为空
	 * @return
	 */
	public boolean isNull() {
		return bIsNull;
	}

	/**
	 * 返回长度
	 * @return
	 */
	public int getPercesion() {
		return this.iPrecision;
	}

	/**
	 * 	 返回精度 
	 * @return
	 */
	public int getScale(){
		return this.iScale;
	}
	
	/**
	 * 设置数据元的长度
	 * @param length 1<=length<=38
	 * @throws ErrorMessage
	 */
	public void setLength(int length) throws ErrorMessage {

		switch(Integer.signum(length)){
		case -1:
			throw new ErrorMessage("Decimal type length cannot is negative !");
		case 1:
			if(length>DECIMAL_MAX_PRECISION){
				throw new ErrorMessage("Decimal type length cannot bigger than "+DECIMAL_MAX_PRECISION+" !");
			}else{
				this.iPrecision=length;
			}
			break;
		case 0:
			throw new ErrorMessage("Decimal type length cannot is 0 !");
		default:
			throw new ErrorMessage("Unknow signum!");
		}
	}

	/**
	 * 设置值，设置值之前先判断值是否有效，无效则抛出异常
	 * @param value 
	 * @throws ErrorMessage
	 */
	public void setValue(String value) throws ErrorMessage {
		/** 存储值为Java值类型 */
		BigDecimal bigValue=null;

		StringBuilder sb=new StringBuilder();
		/** 判断值是否有效,无效则抛出异常 */
		if(!valueInvalid(value,sb)){
			throw new ErrorMessage(sb.toString());
		}
		/**
		 * 如果iScale小于0，精确到小数点左边iScale位，并四舍五入。然后检验有效位是否 <= iPrecision + |iScale|，如果超出，抛出数据溢出异常
		 * 如果iScale大于0，精确到小数点右边iScale位，并四舍五入。然后检验有效位是否 <= iPrecision，如果超出，抛出数据溢出异常
		 */
		if(this.iScale<=0){//类型为取整
			/** 根据精度将值转换成BigDecimal类型 */
			bigValue=new BigDecimal(value,new MathContext(this.iPrecision-this.iScale));
			BigDecimal divideValue=new BigDecimal(Math.pow(10, Math.abs(this.iScale)));
			/** 小数点向左移动|iScale|位，四舍五入取整 */
			bigValue=bigValue.divide(divideValue,0,BigDecimal.ROUND_HALF_UP);
			/** 小数点向右移动|iScale|位 */
			bigValue=bigValue.movePointRight(Math.abs(this.iScale));
			/** 如果值超过范围抛出异常 */
			if(bigValue.precision()>this.iPrecision-this.iScale){
				sb=new StringBuilder();
				sb.append("The value(");
				sb.append(value);
				sb.append(") is out of bound!");
				throw new ErrorMessage(sb.toString());
			}
		}else{//类型含小数位
			/** 取小数点后iScale位，按四舍五入方式进位 */
			bigValue=new BigDecimal(value,new MathContext(this.iPrecision));
			bigValue=bigValue.setScale(this.iScale,BigDecimal.ROUND_HALF_UP);
			/** 如果四舍五入后值超出范围抛出异常 */
			if(bigValue.precision()>this.iPrecision){
				sb=new StringBuilder();
				sb.append("The value(");
				sb.append(value);
				sb.append(") is out of bound!");
				throw new ErrorMessage(sb.toString());
			}
		}

		this.sValue=bigValue.toString();
		/** 设置空标志 */
		this.bIsNull=false;
	}

	/**
	 * 判断是是否有效，不能为空;如果scale>0值的长度必须小于precision;如果scale<0,值必须为整数且值的长度小于precision+scale
	 * @param value 值
	 * @param sp 
	 * @return
	 */
	private boolean valueInvalid(String value,StringBuilder sp){
		boolean isValid=true;
		/** 空值无效 */
		if(value==null){
			isValid=false;
			sp.append("The String parameter is null !");
		}else{
			String[] aRv=value.split("[.]");
			switch(aRv.length){
			case 1://值无小数
				if(iScale>=0){//如果Scale大于0，值的长度>Precision，值无效
					if(aRv[0].length()>this.iPrecision){
						sp.append("The value(");
						sp.append(value);
						sp.append(") is out of bound !");
						isValid=false;
					}
				}else{//如果Scale小于0,值的长度>Precision+|Scale|，值无效
					if((aRv[0].length())>this.iPrecision-iScale){
						sp.append("The value(");
						sp.append(value);
						sp.append(") is out of bound !");
						isValid=false;
					}
				}
				break;
			case 2://值有小数
				if(iScale>=0){//如果Scale大于0，值的整数部分长度+Scale>Precision,值无效
					if((aRv[0].length()+iScale)>this.iPrecision){
						sp.append("The value(");
						sp.append(value);
						sp.append(") is out of bound !");
						isValid=false;
					}
				}else{//如果Scale小于0,值必须为整数返回异常
					sp.append("The value(");
					sp.append(value);
					sp.append(") is not a integer value !");
					isValid=false;
				}
				break;
			default://小数点多于1个，值无效
				sp.append("The value(");
			sp.append(value);
			sp.append(") is not a valid number value !");
			isValid=false;
			break;
			}
		}
		return isValid;
	}

	/**
	 * 设置Decimal的小数位数
	 * @param scale -6<scale<32
	 * @throws ErrorMessage
	 */
	public void setScale(int scale) throws ErrorMessage{
		if(scale>DECIMAL_MAX_SCALE){
			StringBuilder sb=new StringBuilder();
			sb.append("Number type scale(");
			sb.append(scale);
			sb.append(") cannot big than max scale(");
			sb.append(DECIMAL_MAX_SCALE);
			sb.append(") !");
			throw new ErrorMessage(sb.toString());
		}else if(scale<DECIMAL_MIN_SCALE){
			StringBuilder sb=new StringBuilder();
			sb.append("Number type scale(");
			sb.append(scale);
			sb.append(") cannot min than min scale(");
			sb.append(DECIMAL_MIN_SCALE);
			sb.append(") !");
			throw new ErrorMessage(sb.toString());
		}else{
			this.iScale=scale;
		}
	}
	
	/**
	 * 返回值的字符串表示
	 * @return
	 */
	public String toString(){
		if(!this.bIsNull | this.sValue==null){
			return "0";
		}else{
			return this.sValue;
		}
	}
}
